Skip to content

Allow Command-Escape keybindings - #4307

Closed
jakeleventhal wants to merge 17 commits into
pingdotgg:mainfrom
jakeleventhal:t3code/allow-esc-keybindings
Closed

Allow Command-Escape keybindings#4307
jakeleventhal wants to merge 17 commits into
pingdotgg:mainfrom
jakeleventhal:t3code/allow-esc-keybindings

Conversation

@jakeleventhal

@jakeleventhal jakeleventhal commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Screen.Recording.2026-07-22.at.4.30.09.PM.mov
  • allow modified Escape to be recorded as mod+esc while keeping bare Escape as the recorder cancel action
  • bridge native Command-Escape input into the focused Electron renderer, including preview webviews
  • register the macOS shortcut only while T3 Code is focused so it is not claimed while the app is in the background

Root cause

macOS consumes a physical Command-Escape chord before Chromium emits the renderer keyboard event. The keybinding recorder therefore stayed blank even though its web-level normalization supported mod+esc.

Validation

  • vp test run apps/web/src/components/settings/KeybindingsSettings.logic.test.ts apps/desktop/src/keybindings/NativeKeybindingCapture.test.ts apps/desktop/src/preview/Manager.test.ts apps/desktop/src/window/DesktopWindow.test.ts (56 tests)
  • targeted vp lint for all changed files
  • vp run typecheck in apps/desktop
  • vp run typecheck in apps/web
  • manually verified a physical Command-Escape chord records mod+esc in T3 Code Dev

Note

Allow mod+esc keybindings and prevent modified Escape from dismissing UI

  • Added isEscapeDismissal and cancelNonDismissalEscape in keybindings.ts to distinguish plain Escape from modified Escape (e.g., Cmd+Esc)
  • Updated Base UI primitives and custom components to only dismiss on plain Escape, allowing modified Escape to propagate
  • Added NativeKeybindingCapture.ts and updated DesktopWindow.ts and Manager.ts to intercept modified Escape at the Electron level and forward it to the web app for keybinding capture
  • Behavioral Change: Escape with metaKey (macOS) or ctrlKey (other platforms) no longer closes dialogs, menus, or settings panels; it is now captured as a keybinding. Any component not updated to use isEscapeDismissal will still treat modified Escape as dismissal

Macroscope summarized 9ce96d8.


Note

Medium Risk
Touches global shortcuts and broad Escape handling across the app; incorrect dismissal logic could leave dialogs open or break cancel flows, but changes are covered by targeted tests.

Overview
macOS never delivers Command+Escape to the renderer, so this PR captures it in Electron and re-injects a synthetic keydown so shortcuts can be recorded and used as mod+esc.

On macOS, the main window registers the full Command+modifier+Escape set via globalShortcut while the app is focused (unregister on blur/close). Preview webviews and non-macOS main windows use before-input-event to preventDefault and IPC desktop:native-keybinding-capture through preload into the renderer. Capture targets data-keybinding-capture when that input is focused, otherwise window.

The web layer adds isEscapeDismissal and cancelNonDismissalEscape so plain Escape still cancels UI, but platform-mod+Escape is not treated as dismiss. That logic is wired through keybinding recorders, many dismiss handlers, and Base UI roots (dialog, menu, combobox, etc.). keybindingFromKeyboardEvent already maps modified Escape to mod+esc; bare Escape stays null for cancel during capture.

Reviewed by Cursor Bugbot for commit 9ce96d8. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f009ee1-f17a-44b9-8680-091a3e4c1064

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Jul 22, 2026
@jakeleventhal
jakeleventhal marked this pull request as ready for review July 22, 2026 20:38
@macroscopeapp

macroscopeapp Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces native/global shortcut registration, cross-process keyboard routing, and broad changes to Escape dismissal behavior across shared UI primitives and production components. Its cross-platform lifecycle and behavioral scope warrant human review despite targeted test coverage.

You can add or adjust custom eligibility rules. Learn more.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d9d2d6ae96

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/preview/Manager.ts Outdated
Comment thread apps/desktop/src/keybindings/NativeKeybindingCapture.ts Outdated
Comment thread apps/desktop/src/window/DesktopWindow.ts Outdated
Comment thread apps/desktop/src/window/DesktopWindow.ts Outdated
Comment thread apps/desktop/src/window/DesktopWindow.ts
@jakeleventhal
jakeleventhal force-pushed the t3code/allow-esc-keybindings branch from 70f663e to 8d85b2c Compare July 29, 2026 16:02
Comment thread apps/desktop/src/window/DesktopWindow.ts Outdated
Comment thread apps/web/src/keybindings.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8d85b2c618

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/keybindings/NativeKeybindingCapture.ts
Comment thread apps/desktop/src/window/DesktopWindow.ts
Comment thread apps/desktop/src/keybindings/NativeKeybindingCapture.ts
@jakeleventhal
jakeleventhal force-pushed the t3code/allow-esc-keybindings branch from d25021f to d83a665 Compare August 10, 2026 20:25
@t3dotgg t3dotgg added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. and removed vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Aug 24, 2026
…flict-comment-fixes

# Conflicts:
#	apps/desktop/src/window/DesktopWindow.ts
#	apps/web/src/components/CommandPalette.tsx
#	apps/web/src/routes/settings.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One consistency issue found in the keybinding recorder: it now accepts Escape combinations that isEscapeDismissal still classifies as ordinary dismissal, so those bindings collide with the app-wide Escape handling this PR just standardized. Details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/settings/KeybindingsSettings.tsx
Comment thread apps/web/src/components/settings/KeybindingsSettings.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new isEscapeDismissal contract is only applied to about half of the app's Escape-dismissal handlers, so mod+esc still double-fires at the remaining sites in browser mode. Details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/keybindings.ts

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the project-script keybinding recorder is now part of the native mod+esc capture contract but does not follow the isEscapeDismissal policy the two settings recorders adopted, so it can still record Escape combos that the rest of the app treats as dismissals.

The previously flagged issues (incomplete isEscapeDismissal adoption across web Escape handlers, and the settings recorders accepting non-mod Escape combos) are addressed at this head.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/projectScriptEditor.tsx
Comment thread apps/web/src/components/projectScriptEditor.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One consistency gap on the new mod+esc reservation: Base UI's own Escape dismissal paths are still ungated except in projectScriptEditor.tsx, so in the browser mod+esc continues to dismiss overlays and the new gate in CommandPalette has no effect. Details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/CommandPalette.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new mod+esc dismissal gate added to the model picker is attached to the inline Combobox, which never emits an escape-key open-change, while the Popover that actually owns the picker's visibility stays ungated. Details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/chat/ModelPickerContent.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new mod+esc reservation is installed per call site on only three Base UI overlays, so identical overlays elsewhere still dismiss on mod+esc. Detail inline on apps/web/src/components/chat/ProviderModelPicker.tsx.

The raw event.key === "Escape" migration itself looks complete now — no ungated "Escape" comparisons remain under apps/web/src, and both keybinding recorders plus projectScriptEditor now cancel recording through isEscapeDismissal, so non-mod Escape chords are no longer recordable while being treated as dismissals elsewhere.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/chat/ProviderModelPicker.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two gaps in the new mod+esc reservation, both in the shared overlay layer.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/keybindings.ts
Comment thread apps/web/src/components/ui/menu.tsx
@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 26, 2026

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new macOS Escape shortcut wiring in apps/desktop/src/window/DesktopWindow.ts registers app-level Electron listeners through the raw Electron.app global instead of the ElectronApp service the module already acquires from the environment.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/desktop/src/window/DesktopWindow.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new macOS Escape shortcut wiring in apps/desktop/src/window/DesktopWindow.ts registers app-level Electron listeners through the raw Electron.app global instead of the ElectronApp service the module already acquires from the environment.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/desktop/src/window/DesktopWindow.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9ce96d8. Configure here.

{
accelerator: "Command+Alt+Escape",
input: { key: "Escape", metaKey: true, ctrlKey: false, altKey: true, shiftKey: false },
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Force Quit shortcut stolen on macOS

High Severity

MACOS_MOD_ESCAPE_SHORTCUTS registers Command+Alt+Escape with globalShortcut whenever T3 Code is focused. That chord is macOS Force Quit, and Electron can intercept it, so Force Quit is suppressed exactly while this app has focus—the case where users need it most if the UI hangs.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9ce96d8. Configure here.

<Label htmlFor="script-keybinding">Keybinding</Label>
<Input
id="script-keybinding"
data-keybinding-capture=""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recorder bypassed by ChatView capture

Medium Severity

Native mod+esc is dispatched to the project-script input marked data-keybinding-capture, but ChatView’s capture-phase shortcut handler never skips that attribute (unlike AppSidebarLayout). From the chat header script editor, an already-bound mod+esc can run via resolveShortcutCommand before the recorder sees the event.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9ce96d8. Configure here.

@jakeleventhal

Copy link
Copy Markdown
Contributor Author

Closing in favor of a fresh, substantially simplified replacement PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants